#ifndef XG_HTTPFRAME_CPP
#define XG_HTTPFRAME_CPP
//////////////////////////////////////////////////////////
#include "../HttpFrame.h"
#include "../HttpServer.h"

int HttpFrame::decode() const
{
	int sz = 0;
	int bs = 0;
	int idx = 0;
	int fix = 0;
	u_char tag = 0;
	const char* str = data.str();
	const char* end = str + hdrsz;
	static HttpServer* app = HttpServer::Instance();

	while (str < end)
	{
		tag = (u_char)(*str);

		if ((tag & 128) == 128)
		{
			fix = 7;
		}
		else if ((tag & (128 + 64)) == 64)
		{
			fix = 6;
		}
		else if ((tag & (128 + 64 + 32)) == 32)
		{
			fix = 5;
		}
		else
		{
			fix = 4;
		}

		if ((idx = HttpCoder::DecodeInteger(str, bs = end - str, fix)) < 0) return idx;

		if ((str += bs) <= end)
		{
			if (fix == 5)
			{
				vec.size(idx);
			}
			else if (fix == 7)
			{
				const HttpHeadItem& item = vec.get(idx);
				if (item.key.empty()) return XG_DATAERR;
				head.setValue(item.key, item.val, true);
			}
			else
			{
				HttpHeadItem item;

				if (idx > 0)
				{
					item.key = stdx::trim(vec.get(idx).key);
				}
				else
				{
					if ((sz = HttpCoder::GetString(str, end, item.key)) < 0) return XG_DATAERR;

					str += sz;
				}

				if (item.key.empty()) return XG_DATAERR;

				if ((sz = HttpCoder::GetString(str, end, item.val)) < 0) return XG_DATAERR;

				if (fix == 6) vec.push(item);

				head.setValue(item.key, item.val, true);
				str += sz;
			}
		}
	}

	if ((path = HttpRequest::ParsePath(head.getValue(":path"), param)).empty()) return XG_DATAERR;

	CgiMapData cgi = app->getCgiMapData(path);

	maxcnt = cgi.maxcnt;
	maxsz = cgi.maxsz;

	return data.size();
}
E_HTTP_METHOD HttpFrame::getMethod() const
{
	const string& name = head.getValue(":method");

	if (name == "GET") return eGET;
	if (name == "PUT") return ePUT;
	if (name == "POST") return ePOST;
	if (name == "HEAD") return eHEAD;
	if (name == "DELETE") return eDELETE;
	if (name == "OPTIONS") return eOPTION;

	return eGET;
}
int HttpFrame::init(const void* msg, int len)
{
	if (len < HTTP2_HEAD_SIZE) return XG_DATAERR;

	HttpFrameData* obj = (HttpFrameData*)(msg);

	type = obj->type;
	flag = obj->flag;
	id = obj->getId();

	int sz = obj->getSize();
	const char* str = (const char*)(msg) + HTTP2_HEAD_SIZE;

	if (sz <= 0 || len < sz + HTTP2_HEAD_SIZE) return XG_DATAERR;

	if (hasPadding() && (sz -= (u_char)(*str++) + 1) <= 0) return XG_DATAERR;

	if (hasPriority())
	{
		if (sz <= 5) return XG_DATAERR;

		rid = ntohl(*(u_int32*)(str));
		BIT_SET(rid, 31, 0);
		str += 4;
		weight = (u_char)(*str++);
		sz -= 5;
	}

	memcpy(data.malloc(sz), str, sz);

	if (isHeadFrame())
	{
		hdrsz = sz;
		datsz = 0;
	}
	else
	{
		datsz = sz;
		hdrsz = 0;
	}

	if (isEndHead() && decode() < 0) return XG_DATAERR;

	return data.size();
}
int HttpFrame::add(const HttpFrame& obj) const
{
	static HttpServer* app = HttpServer::Instance();

	if (id == obj.id && (obj.isAddFrame() || obj.isDataFrame()))
	{
		if (obj.isDataFrame())
		{
			if (isEndStream() || head.empty()) return XG_DATAERR;

			if (obj.data.size() > 0)
			{
				datsz += obj.data.size();

				if (maxsz > 0 && hdrsz + datsz > maxsz) return XG_DATAERR;
				if (maxcnt > 0 && app->getTransCountItem(path).realtime >= maxcnt) return XG_SYSBUSY;

				data.append(obj.data);
			}

			flag |= obj.flag;
			type = obj.type;
		}
		else
		{
			if (isEndHead() || datsz > 0) return XG_DATAERR;

			hdrsz += obj.data.size();

			if (hdrsz > HTTP_REQHDR_MAXSIZE) return XG_DATAERR;

			if (obj.data.size() > 0) data.append(obj.data);

			flag |= obj.flag;

			if (isEndHead() && head.empty() && decode() < 0) return XG_DATAERR;
		}

		return data.size();
	}

	return XG_FAIL;
}
SmartBuffer HttpFrame::Encode(u_int32 id, HttpHeadNode& head, SmartBuffer data, HttpHeadVector& vec)
{
	string& status = head[":status"];
	string& length = head["Content-Length"];

	if (status.empty()) status = "200";
	if (length.empty()) length = stdx::str(data.size());

	u_char idx = 0;
	SmartBuffer msg;
	const auto& mdat = head.getDataMap();
	u_char buffer[HTTP_REQHDR_MAXSIZE  + HTTP2_HEAD_SIZE];

	u_char* end = buffer + sizeof(buffer);
	u_char* str = buffer + HTTP2_HEAD_SIZE;
	HttpFrameData* hdr = (HttpFrameData*)(buffer);

	for (auto& item : mdat)
	{
		if (item.first.empty() || item.second.empty()) continue;

		string key = item.first;
		string val = item.second;

		if (stdx::tolower(key).find("set-cookie:") == 0) key = "set-cookie";

		*str++ = idx = vec.index(key, val);

		if (str + key.length() + val.length() + 8 > end) return msg;

		if (idx == 0)
		{
			str += HttpCoder::EncodeInteger(str, key.length());
			memcpy(str, key.c_str(), key.length());
			str += key.length();

			str += HttpCoder::EncodeInteger(str, val.length());
			memcpy(str, val.c_str(), val.length());
			str += val.length();
		}
		else
		{
			if (idx < 128)
			{
				str += HttpCoder::EncodeInteger(str, val.length());
				memcpy(str, val.c_str(), val.length());
				str += val.length();

				if (idx > 64) vec.push(HttpHeadItem(key, val));
			}
		}
	}

	u_int32 sz = str - buffer;

	hdr->setId(id);
	hdr->flag = HTTP2_END_HEADER;
	hdr->type = HTTP2_HEADER_FRAME;
	hdr->setSize(sz - HTTP2_HEAD_SIZE);

	if (data.isNull())
	{
		if (stdx::atoi(length.c_str()) == 0) hdr->flag |= HTTP2_END_STREAM;

		memcpy(msg.malloc(sz), buffer, sz);

		return msg;
	}

	u_int32 fs = HTTP2_FRAME_MAXSIZE;
	u_int32 cnt = (data.size() + fs - 1) / fs;
	u_int32 len = sz + cnt * HTTP2_HEAD_SIZE + data.size();

	memcpy(msg.malloc(len), buffer, sz);

	hdr = (HttpFrameData*)(msg.str() + sz);
	sz = data.size();

	u_int32 num = 0;
	u_int32 readed = 0;

	while (readed < sz)
	{
		num = min(fs, sz - readed);

		memcpy(hdr->data, data.str() + readed, num);
		hdr->type = HTTP2_DATA_FRAME;
		hdr->setSize(num);
		hdr->setId(id);
		hdr->flag = 0;

		if ((readed += num) >= sz)
		{
			hdr->flag = HTTP2_END_STREAM;

			break;
		}
		
		hdr = (HttpFrameData*)(hdr->data + num);
	}

	return msg;
}
//////////////////////////////////////////////////////////
#endif
